package gateway.config.security;

import common.util.PublicUrlAntPatternUtil;
import common.util.SecurityConstants;
import gateway.config.security.auth.*;
import gateway.config.security.login.CustomAuthenticationFilter;
import gateway.config.security.logout.CustomLogoutHandler;
import gateway.config.security.logout.CustomLogoutSuccessHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;

import javax.annotation.Resource;

/**
 * Security配置
 *
 * @author 米泽鹏
 * @since 2021-07-31 2:37 下午
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	/**
	 * 用户密码校验过滤器
	 */
	@Resource
	private CustomAuthenticationFilter customAuthenticationFilter;

	/**
	 * 自定义注销处理器
	 */
	@Resource
	private CustomLogoutHandler customLogoutHandler;

	/**
	 * 注销成功处理
	 */
	@Resource
	private CustomLogoutSuccessHandler customLogoutSuccessHandler;

	/**
	 * 访问权限认证异常处理
	 */
	@Resource
	private CustomAuthenticationExceptionHandler customAuthenticationExceptionHandler;

	/**
	 * 认证权限处理 - 将上面所获得角色权限与当前登录用户的角色做对比，如果包含其中一个角色即可正常访问
	 */
	@Resource
	private CustomAccessDeniedHandler customAccessDeniedHandler;

	/**
	 * 获取访问url所需要的角色信息
	 */
	@Resource
	private CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;

	/**
	 * 认证权限处理 - 将上面所获得角色权限与当前登录用户的角色做对比，如果包含其中一个角色即可正常访问
	 */
	@Resource
	private CustomAccessDecisionManager customAccessDecisionManager;

	/**
	 * 访问鉴权 - 认证token、签名...
	 */
	@Resource
	private CustomOncePerRequestFilter customOncePerRequestFilter;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
				// 登录配置
				.formLogin()
				// usernameParameter 会被 CustomAuthenticationFilter 中的配置覆盖，这里配的会失效
				.usernameParameter(SecurityConstants.FORM_USERNAME_KEY)
				// loginPage和loginProcessingUrl的默认值都是/login
				.loginPage(SecurityConstants.LOGIN_PAGE).loginProcessingUrl(SecurityConstants.LOGIN_PROCESSING_URL)
				// 注销配置
				// 配置了logoutSuccessHandler后logoutSuccessUrl就不再生效
				.and().logout().addLogoutHandler(customLogoutHandler).logoutUrl(SecurityConstants.LOGOUT_PROCESSING_URL).logoutSuccessHandler(customLogoutSuccessHandler).deleteCookies().clearAuthentication(true)
				// 禁用CSRF，开启跨域
				.and().csrf().disable().cors()
				// 自定义登录验证
				.and().addFilterAt(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
				// 自动登录 - cookie储存方式
				.rememberMe()
				// 防止iframe 造成跨域
				.and().headers().frameOptions().disable()
				// 未登录认证异常
				.and().exceptionHandling().authenticationEntryPoint(customAuthenticationExceptionHandler)
				// 登录过后访问无权限的接口时自定义响应内容
				.and().exceptionHandling().accessDeniedHandler(customAccessDeniedHandler)
				// 访问鉴权
				.and().addFilterBefore(customOncePerRequestFilter, LogoutFilter.class);
//				.and().addFilterBefore(customOncePerRequestFilter, BasicAuthenticationFilter.class);
		ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
		// 公共url放行，不会绕开spring security验证，相当于进入filter后再放行
		registry.antMatchers(PublicUrlAntPatternUtil.getAllPatterns().toArray(new String[0])).permitAll();
		// 其余所有请求都需要认证
		registry.anyRequest().authenticated();
		// url权限认证
		registry.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
			@Override
			public <O extends FilterSecurityInterceptor> O postProcess(O o) {
				o.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
				o.setAccessDecisionManager(customAccessDecisionManager);
				return o;
			}
		});
	}

//	/**
//	 * 静态资源放行
//	 * <br>
//	 * 绕开spring security的所有filter，直接跳过验证
//	 */
//	@Override
//	public void configure(WebSecurity web) {
//		web.ignoring().antMatchers(
//				"/static/**",
//				"/webjars/**",
//				"/**/*.png",
//				"/**/*.gif",
//				"/**/*.ftl",
//				"/**/*.ttf",
//				"/**/*.html",
//				"/**/*.ico",
//				"/**/*.css",
//				"/**/*.js");
//	}

}
